class Promise {
  constructor(excutor){
    this.status = 'pending'
    this.data = null
    this.callbacks = []
    const _this = this

    function resolve(value) {
      if(_this.status !== 'pending')return
      _this.status = 'resolved'
      _this.data = value
      if(_this.callbacks.length > 0){
        setTimeout(() => {
          _this.callbacks.forEach(item => {
            item.onResolved(value)
          })
        })
      }
    }
    function reject(reason) {
      if(_this.status !== 'pending')return
      _this.status = 'rejected'
      _this.data = reason
      if(_this.callbacks.length > 0){
        setTimeout(() => {
          _this.callbacks.forEach(item => {
            item.onRejected(reason)
          })
        })
      }
    }

    try {
      excutor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onResolved, onRejected){
    if(typeof onResolved !== 'function'){
      onResolved = value => value
    }
    if(typeof onRejected !== 'function'){
      onRejected = reason => {throw reason}
    }
    const _this = this
    return new Promise((resolve, reject) => {
      const handle = function (callback) {
        try {
          let result = callback(_this.data)
          if(result instanceof Promise){
            result.then(resolve, reject)
          } else {
            resolve(result)
          }
        } catch (error) {
          reject(error)
        }
      }
      if(this.status === 'pending'){
        this.callbacks.push({
          onResolved(value){
            handle(onResolved)
          },
          onRejected(reason){
            handle(onRejected)
          }
        })
      } else if (this.status === 'resolved') {
        setTimeout(() => {
          handle(onResolved)
        })
      } else {
        setTimeout(() => {
          handle(onRejected)
        })
      }
    })
  }

  catch(onRejected){
    return this.then(null, onRejected)
  }

  static resolve(value){
    return new Promise((resolve, reject) => {
      if(value instanceof Promise){
        value.then(resolve, reject)
      } else {
        resolve(value)
      }
    })
  }

  static reject(reason){
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }

  static all(promises){
    let arr = new Array(promises.length)
    let step = 0
    return new Promise((resolve, reject) => {
      promises.forEach((item, index) => {
        Promise.resolve(item).then(res => {
          step++
          arr[index] = res
          if(step === promises.length){
            resolve(arr)
          }
        }, reason => {
          reject(reason)
        })
      })
    })
  }

  static race(promises){
    return new Promise((resolve, reject) => {
      promises.forEach(item => {
        Promise.resolve(item).then(res => {
          resolve(res)
        }, reason => {
          reject(reason)
        })
      })
    })
  }
}